package com.gitee.wsl.mathematics.matrix.mat4

import com.gitee.wsl.ext.array.DoubleArray
import com.gitee.wsl.ext.list.exactSize
import com.gitee.wsl.io.dataproxy.DataGet
import com.gitee.wsl.io.dataproxy.asProxy
import com.gitee.wsl.mathematics.matrix.M
import com.gitee.wsl.mathematics.matrix.N
import com.gitee.wsl.mathematics.matrix.mat2.Mat2
import com.gitee.wsl.mathematics.matrix.mat2.MutableMat2
import com.gitee.wsl.mathematics.matrix.mat2.MutableMatrix2
import com.gitee.wsl.mathematics.matrix.mat3.Mat3
import com.gitee.wsl.mathematics.matrix.mat3.MutableMat3
import com.gitee.wsl.mathematics.matrix.mat3.MutableMatrix3
import com.gitee.wsl.mathematics.vector.vec3.Vec3
import com.gitee.wsl.mathematics.vector.vec4.MutableVec4
import com.gitee.wsl.mathematics.vector.vec4.MutableVector4
import com.gitee.wsl.mathematics.vector.vec4.Vec4


open class Mat4(m00: Double=0.0, m01: Double=0.0, m02: Double=0.0,m03: Double=0.0,
                m10: Double=0.0, m11: Double=0.0, m12: Double=0.0,m13: Double=0.0,
                m20: Double=0.0, m21: Double=0.0, m22: Double=0.0,m23: Double=0.0,
                m30: Double=0.0, m31: Double=0.0, m32: Double=0.0,m33: Double=0.0,
                override val proxy: DataGet<Double> =
                    DoubleArray(
                        m00, m01, m02, m03,
                        m10, m11, m12, m13,
                        m20, m21, m22, m23,
                        m30, m31, m32, m33
                        ).asProxy())
    :Matrix4D<Mat4, Vec4> {

    override val self
        get() = this

    override val m by lazy{listOf(M[0], M[1],M[2], M[3])}

    override val n by lazy {listOf(N[0], N[1],N[2], N[3])}

    override fun createVec(indexes: List<Int>) = Vec4(proxy = proxy.asProxy(indexes))

    override fun createVec3(x: Double, y: Double, z: Double) = Vec3(x, y, z)

    override fun createVec4(block: (MutableVector4<Double, *>) -> Unit): Vec4 {
        val ret = MutableVec4()
        block(ret)
        return ret
    }

    override fun createMat2(block: (MutableMatrix2<Double, *, *>) -> Unit): Mat2 {
        val ret = MutableMat2()
        block(ret)
        return ret
    }


    override fun createMat3(block: (MutableMatrix3<Double, *, *>) -> Unit): Mat3 {
        val ret = MutableMat3()
        block(ret)
        return ret
    }

    override fun create(
        m00: Double, m01: Double, m02: Double, m03: Double,
        m10: Double, m11: Double, m12: Double, m13: Double,
        m20: Double, m21: Double, m22: Double, m23: Double,
        m30: Double, m31: Double, m32: Double, m33: Double,
    ): Mat4 = Mat4(
        m00, m01, m02, m03,
        m10, m11, m12, m13,
        m20, m21, m22, m23,
        m30, m31, m32, m33
    )

    override fun create(values: List<Double>): Mat4
      = Mat4(proxy = values.exactSize(16) { 0.0 }.toDoubleArray().asProxy())

    override fun toMutable() = MutableMat4(this)

    override fun hashCode(): Int =
        m00.hashCode() xor
                (m01.hashCode() xor 0x75A5A5A5) xor
                (m10.hashCode() xor 0x5A5A5A5A) xor
                (m11.hashCode() xor 0x76969696)

    override fun equals(other: Any?) =
        other is Mat4 &&
                m00 approx  other.m00 &&
                m01 approx other.m01 &&
                m10 approx other.m10 &&
                m11 approx other.m11

    override fun toString() = "(($m00, $m01),\n ($m10, $m11))"


    companion object{
        /**
         * 4x4 identity matrix
         */
        val IDENTITY = Mat4(m00 = 1.0, m11 = 1.0, m22 = 1.0, m33 = 1.0)

        /**
         * 4x4 zero matrix
         */
        val ZERO = Mat4()

        /**
         * Create a 4x4 matrix from column vectors.
         *
         * @param c0 The first column vector
         * @param c1 The second column vector
         * @param c2 The third column vector
         * @param c3 The fourth column vector
         */
        fun fromColumnVectors(c0: Vec4, c1: Vec4, c2: Vec4, c3: Vec4): Mat4 =
            Mat4(
                c0.x, c1.x, c2.x, c3.x,
                c0.y, c1.y, c2.y, c3.y,
                c0.z, c1.z, c2.z, c3.z,
                c0.w, c1.w, c2.w, c3.w
            )


    }
}
